package com.forte.fortePoiUtils;

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;

import com.forte.fortePoiUtils.commons.Datas;
import com.forte.fortePoiUtils.exception.ExcelTypeException;
import com.forte.fortePoiUtils.merger.ExcelMerger;
import com.forte.fortePoiUtils.merger.HSSF.HSSFExcelMerger;
import com.forte.fortePoiUtils.merger.XSSF.XSSFExcelMerger;
import com.forte.fortePoiUtils.reader.ExcelReader;
import com.forte.fortePoiUtils.reader.HSSFReader.HSSFReader;
import com.forte.fortePoiUtils.reader.XSSFReader.XSSFReader;
import com.forte.fortePoiUtils.typeEnum.ExcelType;
import com.forte.fortePoiUtils.writer.ExcelWriter;
import com.forte.fortePoiUtils.writer.HSSFWriter.HSSFWriter;
import com.forte.fortePoiUtils.writer.XSSFWriter.XSSFWriter;

/**
 * 供外部使用的对外开放的抽象类，目前提供了获取数种获取读取助手对象和获取写入助手对象的方法<br>
 * 如果有想法，可以自己继承并实现方法。<br>
 * <ul>
 * 	<li>写入助手 ： {@link ExcelWriter}</li>
 * 	<li>读取助手 ： {@link ExcelReader}</li>
 * </ul>
 *
 * 历代版本：
 * <ul>
 *  <li>
 *      <p>2.1</p>
 *      <p>更新日期：2018-10-30</p>
 *      <p>
 *      已修复内容与优化内容:
 *      	<ul>
 *          	<li>读取助手和写入助手都可以使用流对象了</li>
 *      	</ul>
 *      已知缺陷与Bug:
 *          <ul>
 *             <li><b>读取助手无法识别表格标题等干扰因素，若要完整读取必须做到Excel中仅有表格信息</b></li>
 *             <li><b>写入助手无法对数据以外的东西进行设置（如：单元格样式、表格标题等）</b></li>
 *         </ul>
 *      </p>
 *  </li>
 *  <li>
 *      <p>2.0</p>
 *      <p>更新日期：未知</p>
 *      <p>
 *          丰富了写入助手的功能<br>
 *           已知缺陷与Bug:
 *         <ul>
 *             <li><b>读取助手无法识别表格标题等干扰因素，若要完整读取必须做到Excel中仅有表格信息</b></li>
 *         </ul>
 *         已修复内容与优化内容:
 *         <ul>
 *             <li>写入助手已经可以使用流对象了</li>
 *             <li>写入助手的映射可以使用多层级字段映射了（例如：user.child.name）,但是测试数据不足，可能存在bug</li>
 *             <li>由于写入助手可以使用多层级映射，则若要使用映射，写入助手便会只写入映射对象中存在的字段。<br>
 *                 如果传入了映射用的Map对象，则只会输出Map中出现的字段；<br>
 *                 如果没有传入映射用的Map对象，则会输出全部字段。
 *                 </li>
 *         </ul>
 *     </p>
 *  </li>
 *  <li>
 *     <p>1.0</p>
 *     <p>更新日期：未知</p>
 *     <P>
 *         提供简单的读与写功能，<br>
 *         可通过本类获取Excel读取助手与Excel写入助手<br>
 *         已知缺陷与Bug:
 *         <ul>
 *             <li>读取助手无法识别表格标题等干扰因素，若要完整读取必须做到Excel中仅有表格信息</li>
 *             <li><del>写入助手无法使用流对象，只能使用文件对象</del></li>
 *             <li><del>写入助手的映射无法使用多层级字段映射</del></li>
 *             <li><del>写入助手写入的对象中不可包含特殊引用对象，否则会抛出空指针异常或直接写入内存地址</del></li>
 *         </ul>
 *     </P>
 * </li>
 * <ul/>
 *
 * @author ForteScarlet
 * <div>
 *		<ul>
 *  		<li> ---><a href='https://gitee.com/ForteScarlet' target='_block'>码云个人地址</a></li> </li>
 *		</ul>
 * </div>
 */
public class PoiHelper {


	/* ———————————————————————————————————— getWriter ———————————————————————————————————————— */


	/**
	 * 提供一个File对象，使用此对象创建Excel写入操作助手
	 * @param path
	 * File对象，如果文件名称有扩展名，则必须为.xls或.xlsx，如果没有则创建一个随机名称的文件。文件命名规则：日期_10位数随机数字.xlsx<br>
	 * 日期格式：yyyy-MM-dd-HHmmss
	 * @return
	 * Excel写入操作助手
	 * @author ForteScarlet
	 */
	public static ExcelWriter getWriter(File path) throws FileNotFoundException {

		String fileName = path.getName();
		//如果不是一个文件，则直接抛出异常
		if(!path.isFile())
			throw new ExcelTypeException("这不是一个文件！");
		//大于1，有扩展名
		if(fileName.split("\\.").length > 1){
			if(fileName.endsWith("." + Datas.HSSF_END))
				return new HSSFWriter(path);
			if(fileName.endsWith("." + Datas.XSSF_END))
				return new XSSFWriter(path);
			//如果扩展名错误，抛出错误
			else
				throw new ExcelTypeException();
		}else{
			//没有扩展名 , 准备文件名
			String date = new SimpleDateFormat(Datas.DATE_FORMAT).format(new Date());
			//返回XSSF写入助手
			return new XSSFWriter(path.getAbsolutePath() , date + "_" +Datas.getRandomNumStr(10) + ".xlsx");
		}
	}

	/**
	 * 提供一个流对象，使用此对象创建Excel写入操作助手<br>
	 * 对于excel的文件类型提供了{@link ExcelType}枚举对象。<br>
	 * 其中EXCEL_XLS与EXCEL_2003相同，EXCEL_XLSX与EXCEL_2010相同。
	 * @param outputStream
	 * @return
	 */
	public static ExcelWriter getWriter(OutputStream outputStream , ExcelType type){
		//流对象无法判断扩展名等事情，所以在参数中提供选项，并提供默认方法（默认为xlsx）
		switch (type){
			case EXCEL_XLS:case EXCEL_2003:case HSSF:
				//返回HSSF写入助手
				return new HSSFWriter(outputStream);

			case EXCEL_XLSX:case EXCEL_2010:case XSSF:
				//返回XSSF写入助手
				return new XSSFWriter(outputStream);

			default:
				//若扩展名错误，抛出异常，虽然此异常基本不可能触发
				throw new ExcelTypeException();
		}
	}

	/**
	 * 提供一个流对象，使用此对象创建Excel写入操作助手<br>
	 * 对于excel的文件类型提供了{@link ExcelType}枚举对象。<br>
	 * 其中EXCEL_XLS与EXCEL_2003相同，EXCEL_XLSX与EXCEL_2010相同。
	 * 默认为.xlsx类型（2010+）
	 * @param outputStream
	 * @return
	 */
	public static ExcelWriter getWriter(OutputStream outputStream){
		return getWriter(outputStream , ExcelType.EXCEL_XLSX);
	}


	/* ————————————————————————————————————————— getReader ——————————————————————————————————————————————— */


	/**
	 * 获取一个Excel读取助手
	 * @param file
	 * @return
	 * @author ForteScarlet
	 */
	public static ExcelReader getReader(File file) throws FileNotFoundException {
		if(!file.exists() || !file.isFile())
			throw new ExcelTypeException();
		String name = file.getName();
		if(name.endsWith("."+Datas.HSSF_END))
			return new HSSFReader(file);
		else if(name.endsWith("."+Datas.XSSF_END))
			return new XSSFReader(file);
		else
			throw new ExcelTypeException();
	}
	
	
	/**
	 * 获取一个Excel读取助手
	 * @param filePath
	 * @return
	 * @author ForteScarlet
	 */
	public static ExcelReader getReader(String filePath) throws FileNotFoundException {
		File file = new File(filePath);
		if(!file.exists() || !file.isFile())
			throw new ExcelTypeException();
		
		return getReader(file);
	}


	/**
	 * 提供一个流对象，创建一个此流对象的ExcelReader对象
	 * 对于excel的文件类型提供了{@link ExcelType}枚举对象。<br>
	 * 其中EXCEL_XLS与EXCEL_2003相同，EXCEL_XLSX与EXCEL_2010相同。
	 * @param inputStream
	 * @return
	 */
	public static ExcelReader getReader(InputStream inputStream , ExcelType type){
		switch (type){
			case EXCEL_XLS:case EXCEL_2003:
				//返回HSSF读取助手
				return new HSSFReader(inputStream);

			case EXCEL_XLSX:case EXCEL_2010:
				//返回XSSF读取助手
				return new XSSFReader(inputStream);

			default:
				//若扩展名错误，抛出异常，虽然此异常基本不可能触发
				throw new ExcelTypeException();
		}
	}

	/**
	 * 提供一个流对象，创建一个此流对象的ExcelReader对象
	 * 对于excel的文件类型提供了{@link ExcelType}枚举对象。<br>
	 * 其中EXCEL_XLS与EXCEL_2003相同，EXCEL_XLSX与EXCEL_2010相同。
	 * 默认为.xlsx类型（2010+）
	 * @param inputStream
	 * @return
	 */
	public static ExcelReader getReader(InputStream inputStream){
		return getReader(inputStream , ExcelType.EXCEL_XLSX);
	}


	/* ————————————————————————————————————————— getMerger ——————————————————————————————————————————————— */

	/**
	 * 获取合并器
	 * @param outputStream	输出流
	 * @param type			输出类型
	 * @return
	 */
	public static ExcelMerger getMerger(OutputStream outputStream , ExcelType type){
		//流对象无法判断扩展名等事情，所以在参数中提供选项，并提供默认方法（默认为xlsx）
		switch (type){
			case EXCEL_XLS:case EXCEL_2003:case HSSF:
				//返回HSSF写入助手
				return new HSSFExcelMerger(outputStream);

			case EXCEL_XLSX:case EXCEL_2010:case XSSF:
				//返回XSSF写入助手
				return new XSSFExcelMerger(outputStream);

			default:
				//若扩展名错误，抛出异常，虽然此异常基本不可能触发
				throw new ExcelTypeException();
		}
	}

	/**
	 * 提供一个File对象，使用此对象创建Excel合并操作助手
	 * @param path
	 * File对象，如果文件名称有扩展名，则必须为.xls或.xlsx，如果没有则创建一个随机名称的文件。文件命名规则：日期_10位数随机数字.xlsx<br>
	 * 日期格式：yyyy-MM-dd-HHmmss
	 * @return
	 * Excel写入操作助手
	 * @author ForteScarlet
	 */
	public static ExcelMerger getMerger(File path) throws FileNotFoundException {
		String fileName = path.getName();
		//如果不是一个文件，则直接抛出异常
		if(!path.isFile())
			throw new ExcelTypeException("这不是一个文件！");
		//大于1，有扩展名
		if(fileName.split("\\.").length > 1){
			if(fileName.endsWith("." + Datas.HSSF_END))
				return new HSSFExcelMerger(path);
			if(fileName.endsWith("." + Datas.XSSF_END))
				return new XSSFExcelMerger(path);
				//如果扩展名错误，抛出错误
			else
				throw new ExcelTypeException();
		}else{
			//没有扩展名 , 准备文件名
			String date = new SimpleDateFormat(Datas.DATE_FORMAT).format(new Date());
			//返回XSSF
			return new XSSFExcelMerger(new FileOutputStream(new File(path.getAbsolutePath() , date + "_" +Datas.getRandomNumStr(10) + ".xlsx")));
		}
	}


}
